package cn.jiedanba.itext5.gm.sign.test;

import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.Security;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jcajce.provider.digest.SM3;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenInfo;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Hex;
import org.ofdrw.gm.ses.v4.CertInfoList;
import org.ofdrw.gm.ses.v4.SES_Signature;
import org.ofdrw.gm.ses.v4.TBS_Sign;

import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfArray;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfString;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
import com.itextpdf.text.pdf.security.DigestAlgorithms;

import cn.jiedanba.itext5.SignatureAlgorithms;
import cn.jiedanba.itext5.util.PkiUtil;

public class PdfSignVerify {

	public static void main(String[] args) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		Path pdf = Paths.get("src/main/resources", "signPdf2.pdf");
		PdfReader reader = new PdfReader(Files.readAllBytes(pdf));
		// 创建签名工具
		AcroFields acroFields = reader.getAcroFields();
		// 获取所有的签名域，域名称列表
		List<String> fieldNames = acroFields.getSignatureNames();
		for (String fieldName : fieldNames) {
			PdfDictionary v = acroFields.getSignatureDictionary(fieldName);
			PdfString contents = v.getAsString(PdfName.CONTENTS);
			// 获取签名数据
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			ASN1InputStream asn1InputStream = new ASN1InputStream(contents.getBytes());
			ASN1Sequence seq = (ASN1Sequence) asn1InputStream.readObject();

			asn1InputStream.close();
			SES_Signature sesSignature = SES_Signature.getInstance(seq.getEncoded());

			// 签章者证书
			X509Certificate signCert = PkiUtil.readX509Certificate(sesSignature.getCert().getOctets());

			TBS_Sign toSign = sesSignature.getToSign();

			Signature sg = Signature.getInstance(sesSignature.getSignatureAlgID().toString(),
					new BouncyCastleProvider());
			sg.initVerify(signCert);
			sg.update(toSign.getEncoded("DER"));
			byte[] sigVal = sesSignature.getSignature().getBytes();

			System.out.println("签名验证结果：" + sg.verify(sigVal));

			PdfArray b = v.getAsArray(PdfName.BYTERANGE);
			RandomAccessFileOrArray rf = reader.getSafeFile();
			byte buf[] = new byte[8192];
			for (int k = 0; k < b.size(); ++k) {
				int start = b.getAsNumber(k).intValue();
				int length = b.getAsNumber(++k).intValue();
				rf.seek(start);
				while (length > 0) {
					int rd = rf.read(buf, 0, Math.min(length, buf.length));
					if (rd <= 0)
						break;
					length -= rd;
					output.write(buf, 0, rd);
				}
			}
			byte[] srcData = output.toByteArray();

			final ASN1BitString dataHash = sesSignature.getToSign().getDataHash();
			MessageDigest md = new SM3.Digest();
			byte[] digest = md.digest(srcData);
			SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("原文摘要：" + Hex.toHexString(digest));
			System.out.println("签名摘要：" + Hex.toHexString(sesSignature.getToSign().getDataHash().getOctets()));
			System.out.println("原文摘要与签名里的摘要比对结果：" + Arrays.equals(digest, dataHash.getOctets()));
			System.out.println("厂商：" + sesSignature.getToSign().getEseal().geteSealInfo().getHeader().getVid());
			String esealSignOid = sesSignature.getToSign().getEseal().getSignAlgID().getId();
			System.out.println("印章签名算法：" + SignatureAlgorithms.getSignatureName(esealSignOid));
			System.out.println("印章编码：" + sesSignature.getToSign().getEseal().geteSealInfo().getEsID().getString());
			System.out.println("签名时间：" + formater.format(sesSignature.getToSign().getTimeInfo().getDate()));

			System.out.println("签章人：" + signCert.getSubjectDN());
			System.out.println(
					"签章人签名算法：" + SignatureAlgorithms.getSignatureName(sesSignature.getSignatureAlgID().getId()));
			// 制章者证书
			X509Certificate sealCert = PkiUtil
					.readX509Certificate(sesSignature.getToSign().getEseal().getCert().getOctets());

			System.out.println("制章人：" + sealCert.getSubjectDN());

			// 印章里面的签章者证书
			CertInfoList certInfoList = sesSignature.getToSign().getEseal().geteSealInfo().getProperty().getCertList()
					.getCerts();
			for (ASN1OctetString asn1OctetString : certInfoList) {
				X509Certificate ent = PkiUtil.readX509Certificate(asn1OctetString.getOctets());
				Signature sg1 = Signature.getInstance(sesSignature.getSignatureAlgID().toString(),
						new BouncyCastleProvider());
				sg1.initVerify(ent);
				sg1.update(toSign.getEncoded("DER"));
				System.out.println("印章里面的签章人：" + ent.getSubjectDN());
				System.out.println("印章里面的签章者证书与签名值验签：" + sg1.verify(sesSignature.getSignature().getBytes()));

			}
			if (sesSignature.getTimeStamp() != null) {
				TimeStampToken timeStampToken = new TimeStampToken(
						ContentInfo.getInstance(sesSignature.getTimeStamp().getOctets()));
				TimeStampTokenInfo info = timeStampToken.getTimeStampInfo();
				String algOID = info.getHashAlgorithm().getAlgorithm().getId();
				System.out.println("时间戳签名摘要算法:" + DigestAlgorithms.getDigest(algOID));
				MessageDigest messageDigest = new SM3.Digest();
				System.out.println(
						"时间戳是否有效：" + Arrays.equals(messageDigest.digest(sigVal), info.getMessageImprintDigest()));
				System.out.println("时间戳签名时间：" + formater.format(info.getGenTime()));

				Store<?> storeTt = timeStampToken.getCertificates();
				Collection collTt = storeTt.getMatches(timeStampToken.getSID());
				Iterator certIt2 = collTt.iterator();

				X509CertificateHolder cert2 = (X509CertificateHolder) certIt2.next();
				System.out.println(
						"时间戳签名者：" + cert2.getSubject().getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());

			}

			System.out.println();
			System.out.println();

		}
	}
}